<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;

use App\Events\LoggedIn;
use App\Libs\GoogleAuth;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
// use Laravel\Sanctum\HasApiTokens;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
  use  HasFactory, Notifiable;

  protected $guarded = ['id'];

  protected $hidden = [
    'password',
    'remember_token',
    'google_secret',
    'google_auth_enable',
    'api_token',
    // 'code'
  ];

  protected $casts = [
    // 'email_verified_at' => 'datetime',
    'login_at' => 'datetime',
    'enable' => 'boolean',
    'google_auth_enable' => 'boolean',
    'perms' => 'array',
    'coins' => 'float',
    'cut' => 'float',
    'tip' => 'float',
    'buys_frozen_sum_coins' => 'float',
    'solds_sum_coins' => 'float',
    'boughts_sum_coins' => 'float',
    'boughts_sum_tip' => 'float',
    'boughts_sum_fee' => 'float',
    'sells_sum_coins' => 'float',
    'buys_sum_coins' => 'float',
    'buys_sum_fee' => 'float',
    'buys_sum_tip' => 'float',
    'buys_took_sum_coins' => 'float',
    'buys_waiting_sum_coins' => 'float',
    'buys_frozen_sum_deposit' => 'float',
    'tip_fee' => 'float',
    'fee_sum' => 'float',
    'tip_sum' => 'float',
  ];

  public function getJWTIdentifier()
  {
    return $this->getKey();
  }

  public function getJWTCustomClaims()
  {
    return [];
  }

  protected function serializeDate($date)
  {
    return $date->format('Y-m-d H:i');
  }

  public function scopePage($q, $size = 10)
  {
    return $q->paginate(request()->input('page_size', $size));
  }

  public function getFreezeMinutesAttribute($val)
  {
    return $this->role === 'member' ? $this->parent->freeze_minutes : $val;
  }

  public function getCoinsAttribute($val)
  {
    if ($this->role == 'seller' && $this->pid) {
      return $this->parent->coins;
    }
    return $val;
  }

  public function parent()
  {
    return $this->belongsTo(self::class, 'pid');
  }

  public function whitelist()
  {
    return $this->hasMany(Whitelist::class);
  }

  public function violations()
  {
    return $this->hasMany(Violation::class, 'member_id');
  }

  public function scopeWithSumBetween($q, $relation, $field, $range, $date_filed)
  {
    $snake = \Str::snake($relation);
    $q->withCount(["{$relation} as {$snake}_sum_{$field}" => function ($query) use ($date_filed, $field, $range) {
      $query->whereBetween($date_filed, $range)->select(\DB::raw("sum({$field})"));
    }]);
  }

  public function scopeWithCountBetween($q, $relations, $range, $date_filed)
  {
    $relations = \Arr::wrap($relations);
    // dump($relations);
    foreach ($relations as $relation) {
      $snake = \Str::snake($relation);
      $q->withCount(["{$relation} as {$snake}_count" => function ($query) use ($date_filed, $range) {
        $query->whereBetween($date_filed, $range);
      }]);
    }
  }

  public function calcOrder($coins)
  {
    $fee = bcmul($coins, $this->fee_ratio, 3);
    $total = bcadd($coins, $fee, 3);
    $total = bcadd($total, $this->tip, 3);
    return [$total, $fee, $this->tip];
  }

  public function checkIP()
  {
    $ips = $this->whitelist->pluck('ip');
    if ($ips->count() > 0 && !$ips->contains(request()->getClientIp())) {
      fail('IP不在白名单');
    }
  }

  public function getBankcardAttribute($q)
  {
    return $q->with('bankcards')->latest()->first();
  }

  public function bankcards()
  {
    return $this->hasMany(Bankcard::class);
  }

  public function subs()
  {
    return $this->hasMany(self::class, 'pid');
  }

  public function agentBoughts()
  {
    return $this->hasMany(Buy::class, 'agent_id')->where('state', 'unfreeze');
  }

  public function sells()
  {
    return $this->hasMany(Sell::class, 'member_id');
  }

  public function sellerSells()
  {
    return $this->hasMany(Sell::class, 'seller_id');
  }

  public function solds()
  {
    return $this->hasMany(Sell::class, 'member_id')->where('state', 'done');
  }

  public function buys()
  {
    return $this->hasMany(Buy::class, 'member_id');
  }

  public function sellerBuys()
  {
    return $this->hasMany(Buy::class, 'seller_id');
  }

  public function sellerBuysDone()
  {
    return $this->hasMany(Buy::class, 'seller_id')->whereIn('state', ['unfreeze', 'fail']);
  }

  public function sellerBuysUndone()
  {
    return $this->hasMany(Buy::class, 'seller_id')->whereNotIn('state', ['unfreeze', 'fail']);
  }

  public function boughts()
  {
    return $this->hasMany(Buy::class, 'member_id')->where('state', 'unfreeze');
  }

  public function buysFrozen()
  {
    return $this->hasMany(Buy::class, 'member_id')->where('state', 'frozen');
  }

  public function buysTook()
  {
    return $this->hasMany(Buy::class, 'member_id')->where('state', 'took');
  }

  public function buysWaiting()
  {
    return $this->hasMany(Buy::class, 'member_id')->where('state', 'waiting');
  }

  // public function reservesSum()
  // {
  //   $arr = $this->buys()
  //     ->where('state', 'frozen')
  //     ->first(
  //       [
  //         \DB::raw('SUM(coins) as coins'),
  //         \DB::raw('SUM(fee) as fee'),
  //         \DB::raw('SUM(tip) as tip'),
  //       ]
  //     )->toArray();
  //   return array_sum($arr);
  // }

  public function calcCoins($top_up)
  {
    $fee = bcmul(abs($top_up), $this->fee_ratio, 3);
    $coins = bcsub(bcadd($this->coins, $top_up), $fee);
    if ($coins < 0) {
      fail('币数不足');
    }
    $this->coins = $coins;
  }

  public function loginInfo()
  {
    if ($this->role == 'member') {
      $fee = $this->buys()->thisMonth()->state('unfreeze')->sum('fee');
      $tip = $this->buys()->thisMonth()->state('unfreeze')->sum('tip');

      $this->setAttribute('profit', bcadd($fee, $tip, 3));
      $this->loadSum('buysFrozen', 'deposit');
      $this->load(['bankcards' => function ($q) {
        $q->latest();
      }]);

      return $this->makeHidden('pid', 'security', 'notify_url', 'parent', 'perms', 'cut');
    }
    if ($this->role == 'seller' && $this->pid) {
      $this->coins = $this->parent->coins;
    }

    return $this;
  }

  public function oprationLogs()
  {
    return $this->hasMany(OprationLog::class);
  }

  public function createLog($inputs)
  {
    if ($inputs['password'] != '') {
      $this->oprationLogs()->create([
        'content' => "修改商户 {$this->username} 密码"
      ]);
    }
    $changes = $this->getChanges();
    if (isset($changes['mobile'])) {
      $this->oprationLogs()->create([
        'content' => "修改商户 {$this->username} 手机号为 {$inputs['mobile']}"
      ]);
    }
    if (isset($changes['code'])) {
      $this->oprationLogs()->create([
        'content' => "修改商户 {$this->username} 编码为 {$inputs['code']}"
      ]);
    }
    if (isset($changes['coins'])) {
      // $action = $inputs['top_up'] > 0 ? '加' : '减';
      $ratio = bcmul($this->fee_ratio, 1000, 2);
      $fee = bcmul(abs($inputs['top_up']), $this->fee_ratio, 3);
      $withFee = bcsub($inputs['top_up'], $fee);

      $this->oprationLogs()->create([
        'content' => "给商户 {$this->username} 人工 {$inputs['top_up']} FCB，手续费 ({$ratio}‰) {$fee} FCB；实际到可动 {$withFee}"
      ]);
    }
    if (isset($changes['fee_ratio'])) {
      $this->oprationLogs()->create([
        'content' => "修改商户 {$this->username} 买币手续费为 {$this->fee_ratio}"
      ]);
    }
    if (isset($changes['enable'])) {
      $act = $this->enable ? '启用' : '禁用';
      $this->oprationLogs()->create([
        'content' => "{$act}商户 {$this->username}"
      ]);
    }
    if (isset($changes['tip'])) {
      $this->oprationLogs()->create([
        'content' => "修改商户 {$this->username} 下发手续费为 {$this->tip}"
      ]);
    }
  }

  public function coinsChange($val, $type, $order_id = null)
  {
    if ($val == 0) {
      return;
    }
    $after = bcadd($this->coins, $val, 3);
    if ($after < 0) {
      fail('币数不足');
    }
    Balance::create([
      'order_id' => $order_id,
      'user_id' => $this->id,
      'type' => $type,
      'value' => $val,
      'b4' => $this->coins,
      'after' => $after
    ]);
    $this->coins = $after;
    $this->save();
  }

  public static function byMobile($mobile)
  {
    return Self::where('mobile', $mobile)
      ->with(['bankcards' => function ($q) {
        $q->latest();
      }])
      ->firstOr(function () {
        fail('用户未注册');
      });
  }

  public function updateLogin()
  {
    Self::whereId($this->id)->update([
      'login_at' => now(),
      'login_ip' => request()->getClientIp(),
    ]);
  }

  public function singleLogin($token)
  {
    \Cache::put("user:{$this->id}:token", $token);
    broadcast(new LoggedIn($this))->toOthers();
  }

  public function checkWhitlist()
  {
    $ips = $this->whitelist()->where('type', 'login')->pluck('ip');
    if ($ips->count() > 0 && !$ips->contains(request()->getClientIp())) {
      fail('用户IP不在登录白名单');
    }
  }

  public function checkGa()
  {
    if ($this->google_secret && $this->google_auth_enable) {
      if (request()->filled('google_code')) {
        GoogleAuth::verify($this->google_secret, request('google_code'));
      } else {
        fail('请输入谷歌验证码');
      }
    }
  }

  public function checkRoleEnable($role)
  {
    if (!$this->enable) {
      fail('已禁用');
    }
    if ($this->role !== $role) {
      forbidden();
    }
  }
}
